#include "tools/constants.hh"
#include "media/Sound.hh"
#include "tools/Vector.hh"

#ifdef DEBUG
#  include "tools/debug_funs.hh"
#endif


// This file is a little bit too C style


// Ctors - Dtor
Sound::Sound(std::string fileName)
  : _fileName(SOUND_PATH_PREFIX + fileName),
    _loop(false),
    _source(0),
    _listenerPos {0, 0, 0},
    _listenerVel {0.0, 0.0, 0.0},
    _listenerOri {0.0, 0.0, -1.0,  0.0, 1.0, 0.0}
{
    if (!(_fp = fopen(_fileName.c_str(), "rb")))
    {
      std::cerr << "Failed to open sound file" << std::endl;
      std::exit(-2);
    }

    init();
    initSource();
    initListener();
}


Sound::Sound(std::string fileName, bool loop)
  : _fileName(SOUND_PATH_PREFIX + fileName),
    _loop(loop),
    _source(0),
    _listenerPos {0, 0, 0},
    _listenerVel {0.0, 0.0, 0.0},
    _listenerOri {0.0, 0.0, -1.0,  0.0, 1.0, 0.0}
{
    if (!(_fp = fopen(_fileName.c_str(), "rb")))
    {
      std::cerr << "Failed to open sound file" << std::endl;
      std::exit(-2);
    }

    init();
    initSource();
    initListener();
}


Sound::~Sound()
{
    fclose(_fp);
    delete[] _buf;
    alDeleteSources(1, &_source); // Delete OpenAL Source
    alDeleteBuffers(1, &_dataBuffer); // Delete OpenAL Buffer
}
// end Ctors - Dtor



// getters - setters

void Sound::setLoop(bool loop)
{
  _loop = loop;
}

// end getters - setters


void Sound::checkFileFormat()
{
    char type[4];
    unsigned int size;

    fread(type, sizeof(char), 4, _fp);
    if (type[0] != 'R' || type[1] != 'I' || type[2] != 'F' || type[3] != 'F')
    {
      std::cerr << "No RIFF" << std::endl;
      std::exit(-2);
    }

    fread(&size, sizeof(int),1,_fp);
    fread(type, sizeof(char),4,_fp);
    if (type[0] != 'W' || type[1] != 'A' || type[2] != 'V' || type[3] != 'E')
    {
      std::cerr << "Not WAVE" << std::endl;
      std::exit(-2);
    }


    // more cpp style *NOT* working test
    // std::string str;
    // std::fread((void*) str.c_str(), sizeof(char), 4, _fp);
    // if (str.compare("fmt "))
    // {
    //   std::cerr << "Not fmt " << str.c_str() << "!" << std::endl;
    //   std::exit(-2);
    // }


    std::fread(type, sizeof(char), 4, _fp);
    if (type[0]!='f' || type[1]!='m' || type[2]!='t' || type[3]!=' ')
    {
      std::cerr << "Not fmt " << type << "!" << std::endl;
      std::exit(-2);
    }
}




bool Sound::init()
{
    checkFileFormat();

    unsigned int avgBytesPerSec;
    unsigned int chunkSize;
    short nbBitsPerSample;
    short nbChannels;
    short nbBytesPerSample; // nbBitsPerSample / 8
    short formatType;

#ifdef DEBUG
    safe_fread(&chunkSize, sizeof(int), 1, _fp);
    safe_fread(&formatType, sizeof(short), 1, _fp);
    safe_fread(&nbChannels, sizeof(short), 1, _fp);
    safe_fread(&_sampleRate, sizeof(int), 1, _fp);
    safe_fread(&avgBytesPerSec, sizeof(int), 1, _fp);
    safe_fread(&nbBytesPerSample, sizeof(short), 1, _fp);
    safe_fread(&nbBitsPerSample, sizeof(short), 1, _fp);
#else
    fread(&chunkSize, sizeof(int), 1, _fp);
    fread(&formatType, sizeof(short), 1, _fp);
    fread(&nbChannels, sizeof(short), 1, _fp);
    fread(&_sampleRate, sizeof(int), 1, _fp);
    fread(&avgBytesPerSec, sizeof(int), 1, _fp);
    fread(&nbBytesPerSample, sizeof(short), 1, _fp);
    fread(&nbBitsPerSample, sizeof(short), 1, _fp);
#endif

	#ifdef DEBUG_SOUND
      std::cout << "__________ Sounds infos about " << _fileName << std::endl;
      std::cout << "Chunk Size: " << chunkSize << std::endl;
      std::cout << "Format Type: " << formatType << std::endl;
      std::cout << "Channels: " << nbChannels << std::endl;
      std::cout << "Sample Rate: " << _sampleRate << std::endl;
      std::cout << "Average Bytes Per Second: " << avgBytesPerSec << std::endl;
      std::cout << "Bytes Per Sample: " << nbBytesPerSample << std::endl;
      std::cout << "Bits Per Sample: " << nbBitsPerSample << std::endl;
      std::cout << "Data Size: " << _dataSize << std::endl;
      std::cout << "__________" << std::endl << std::endl;
    #endif

    fillData();
    alGenBuffers(1, &_dataBuffer);
    alGenSources(1, &_source);
    if (alGetError() != AL_NO_ERROR)
    {
      std::cerr << "Error generating source" << std::endl;
      std::exit(-2);
    }

    alBufferData(_dataBuffer, getFormat(nbBitsPerSample, nbChannels), _buf, _dataSize, _sampleRate);
    if (alGetError() != AL_NO_ERROR)
    {
      std::cerr << "Error loading ALBuffer" << std::endl;
      std::exit(-2);
    }

    return true;
}


void Sound::pause()
{
  alSourcePause(_source);
}


void Sound::stop()
{
  alSourceStop(_source);
}


ALint Sound::getStatus()
{
  ALint* status = NULL;

  alGetSourcei(_source, AL_SOURCE_STATE, status);

  return (*status);
}


ALenum Sound::getFormat(short nbBitsPerSample, short nbChannels)
{
    ALenum format = 0;

    if (nbBitsPerSample == 8)
    {
      if (nbChannels == 1)
	format = AL_FORMAT_MONO8;
      else if (nbChannels == 2)
	format = AL_FORMAT_STEREO8;
    }

    else if (nbBitsPerSample == 16)
    {
      if (nbChannels == 1)
	format = AL_FORMAT_MONO16;
      else if (nbChannels == 2)
	format = AL_FORMAT_STEREO16;
    }

    if (!format)
    {
      std::cerr << "Wrong BitPerSample or Channels; found:" << std::endl;
      std::cerr << nbBitsPerSample << " BitPerSample" << std::endl;
      std::cerr << nbChannels << " nbChannels" << std::endl;
      std::exit(-2);
    }

    return (format);
}


void Sound::initSource()
{
    // Sound setting variables
    ALfloat SourcePos[] = {0.0, 0.0, 0.0}; // Source Position
    ALfloat SourceVel[] = {0.0, 0.0, 0.0}; // Source Velocity

    alSourcei(_source, AL_BUFFER, _dataBuffer);
    alSourcef(_source, AL_PITCH, 1.0f);
    alSourcef(_source, AL_GAIN, 1.0f);
    alSourcefv(_source, AL_POSITION, SourcePos);
    alSourcefv(_source, AL_VELOCITY, SourceVel);

    if (_loop) // do we have to play the sound in loop ?
      alSourcei(_source, AL_LOOPING, AL_TRUE);
    else
      alSourcei(_source, AL_LOOPING, AL_FALSE);
}


void Sound::initListener()
{
    alListenerfv(AL_POSITION, _listenerPos);
    alListenerfv(AL_VELOCITY, _listenerVel);
    alListenerfv(AL_ORIENTATION, _listenerOri);
}



void Sound::setListenerPosition(Coords v)
{
  ALfloat ListenerPos[] = {(ALfloat) v.x(), (ALfloat) v.y(), (ALfloat) v.z()};  // listener position
    ALfloat ListenerVel[] = {0.0, 0.0, 0.0};  // listener velocity
    ALfloat ListenerOri[] = {0.0, 0.0, -1.0,  0.0, 1.0, 0.0}; // listener orientation

    alListenerfv(AL_POSITION, ListenerPos);
    alListenerfv(AL_VELOCITY, ListenerVel);
    alListenerfv(AL_ORIENTATION, ListenerOri);
}


void Sound::play()
{
    alSourcePlay(_source);
    if (alGetError() != AL_NO_ERROR)
    {
      std::cerr << "Error while playing sound" << std::endl;
      std::exit(-2);
    }
}


void Sound::fillData()
{
    char type[4];

    if (!fread(type, sizeof(char), 4, _fp))
    {
      std::cerr << "fread" << std::endl;
      std::exit(-2);
    }

    if (type[0] != 'd' || type[1] != 'a' || type[2] != 't' || type[3] != 'a')
    {
      std::cerr << "Missing DATA" << std::endl;
      std::exit(-2);
    }
    fread(&_dataSize, sizeof(int), 1, _fp);

    _buf = new unsigned char[_dataSize];
    fread(_buf, 1, _dataSize, _fp);
}
